#include "VTTInfoFile.h"
#include "VTTErrorManager.h"
#include "VTTImage.h"
#include "VTTPackNode.h"

#include <fstream>

using std::ifstream;
using std::map;
using std::ofstream;
using std::pair;
using std::string;
using std::vector;

/**
  *	Constructs a new info file object. The path of the info file has to be provided.
  *
  * @param fileName path of the info file
  */
VTTInfoFile::VTTInfoFile( const string &fileName )
{
	mIsNull = true;

	if (!IsFile( fileName ))
	{
		//cout << "ERROR: Info file " << fileName << " does not exist" << endl;
		VTTErrorManager::Get()->pushErrorMessage( "VTTInfoFile::VTTInfoFile(const string &): Info file " + fileName + " does not exist" );
		return;
	}

	LoadFromFile( fileName );
}

/**
  *	Returns a const reference to the map, that maps names (not paths!) of images to the info-structs
  *
  * @return map, that maps names of images to the info-structs
  */
const map<string, struct VTTImageInfo> &VTTInfoFile::GetNamesToInfoMap( void ) const
{
	return mNamesToInfoMap;
}

/**
  *	Sets a time stamp for a particular image name. This is usually the time stamp
  * of the last changing time.
  *
  * @param imageName name (not path!) of the image the time stamp should be set for
  * @param imageInfo the new time stamp for the image
  * @return boolean value indicating whether the image was found or not
  */
bool VTTInfoFile::SetChangeTimeForImageName( const string &imageName, const struct tm &imageInfo )
{
	map<string, struct VTTImageInfo>::iterator it = mNamesToInfoMap.find( imageName );

	if (mNamesToInfoMap.end() == it)
		return false;

	it->second.changeTime = imageInfo;

	return true;
}

/**
  * Loads an image file from disc
  *
  * @param filePath full path of the info file
  * @return boolean value indicating whether the image file could be loaded or not
  */
bool VTTInfoFile::LoadFromFile( const string &filePath )
{
	ifstream file;

	file.open( filePath.c_str() );

	if (!file)
	{
		//cout << "ERROR: Could not read info file " << filePath
		//	<< endl;

		VTTErrorManager::Get()->pushErrorMessage( "VTTInfoFile::LoadFromFile(const string &): Could not read Info file " + filePath + " [ERRNO: " + UINT32ToString( errno ) + "]" );
		Reset();
		return false;
	}

	mFullPath = filePath;
	mBasePath = GetBasePathOfFile( filePath );

	string line_read;
	vector<string> tokens;

	VTTImageInfo info;

	UINT32 current_token_nr = 1U;
	UINT32 occurence = 0U;
	std::tr1::array<UINT32, 4> current_area;

	const UINT32 ELEMENTS_FOR_AREA = 4U;
	const UINT32 ELEMENTS_FOR_CHANGING_DATE = 9U;

	while (std::getline( file, line_read )) // Read line by line
	{
		if (!StringStartsWith( line_read, "#" ))
		{
			tokens.clear();

			Tokenize( line_read, tokens, "/" );

			info.areas.clear();

			// Occurence
			current_token_nr = 1U;
			occurence = StringToInt( tokens[current_token_nr++] );

			if (!(tokens.size() == 2 + (ELEMENTS_FOR_AREA * occurence) + ELEMENTS_FOR_CHANGING_DATE))
			{
				//cout << "ERROR: Invalid number of arguments in single line of Info file"
				//	 << endl;
				VTTErrorManager::Get()->pushErrorMessage( "VTTInfoFile::LoadFromFile(const string &): Invalid number of arguments in single line of Info file" );
				Reset();

				return false;
			}

			for (UINT32 i = 0; i < occurence; i++)
			{
				// Area
				current_area[0] = StringToInt( tokens[current_token_nr++] );
				current_area[1] = StringToInt( tokens[current_token_nr++] );
				current_area[2] = StringToInt( tokens[current_token_nr++] );
				current_area[3] = StringToInt( tokens[current_token_nr++] );

				info.areas.push_back( current_area );
			}

			// Change Time
			info.changeTime.tm_hour = StringToInt( tokens[current_token_nr++] );
			info.changeTime.tm_isdst = StringToInt( tokens[current_token_nr++] );
			info.changeTime.tm_mday = StringToInt( tokens[current_token_nr++] );
			info.changeTime.tm_min = StringToInt( tokens[current_token_nr++] );
			info.changeTime.tm_mon = StringToInt( tokens[current_token_nr++] );
			info.changeTime.tm_sec = StringToInt( tokens[current_token_nr++] );
			info.changeTime.tm_wday = StringToInt( tokens[current_token_nr++] );
			info.changeTime.tm_yday = StringToInt( tokens[current_token_nr++] );
			info.changeTime.tm_year = StringToInt( tokens[current_token_nr++] );

			mNamesToInfoMap.insert( pair<string, VTTImageInfo>( tokens[0], info ) );
 		}
	}

	mIsNull = false;

 	return true;
}

/**
  * Writes an image file to disc
  *
  * @param filePath full path of the info file
  * @return boolean value indicating whether the info file could be written or not
  */
bool VTTInfoFile::WriteToFile( const string &filePath ) const
{
	if (IsNull())
	{
		//cout << "ERROR: Can't write Null Info File" << endl;
		VTTErrorManager::Get()->pushErrorMessage( "VTTInfoFile::WriteToFile(const string &): Can not write Null Info File" );
		return false;
	}

	ofstream info_file;
	info_file.open( (filePath == "" ? mFullPath : filePath) );

	info_file << "# Virtual Texture Atlas Info File, generated by VTTools by Michael Birsak\n#\n";
	info_file << "# <imageName>/<occurrence>/(<areaMinX>/<areaMinY>/<areaExclMaxX>/<areaExclMaxY>/){<occurrence>}<chTime_hour>/<chTime_isdst>/<chTime_mday>/<chTime_min>/<chTime_mon>/<chTime_sec>/<chTime_wday>/<chTime_yday>/<chTime_year>\n";

	map<string, struct VTTImageInfo>::const_iterator it;

	for (it = mNamesToInfoMap.begin(); it != mNamesToInfoMap.end(); it++)
	{
		info_file << it->first << "/" << it->second.areas.size() << "/";

		for (vector<std::tr1::array<UINT32, 4>>::const_iterator area_it = it->second.areas.begin();
			area_it != it->second.areas.end(); area_it++)
		{
			info_file << (*area_it)[0] << "/" << (*area_it)[1] << "/" << (*area_it)[2] << "/"
				<< (*area_it)[3] << "/";
		}

		info_file << it->second.changeTime.tm_hour << "/"
			<< it->second.changeTime.tm_isdst << "/" << it->second.changeTime.tm_mday << "/"
			<< it->second.changeTime.tm_min << "/" << it->second.changeTime.tm_mon << "/"
			<< it->second.changeTime.tm_sec << "/" << it->second.changeTime.tm_wday << "/"
			<< it->second.changeTime.tm_yday << "/" << it->second.changeTime.tm_year << "\n";
	}

	info_file.close();

	return true;
}

/**
  *	Resets all member variables. The info file is a Null-object after that.
  */
void VTTInfoFile::Reset( void )
{
	mBasePath = "";
	mFullPath = "";
	mIsNull = true;
	mNamesToInfoMap.clear();
}

/**
  *	Writes an info file based on the information in the provided map.
  *
  * @param directoryPath Path of the directory where the info file is stored into
  * @param imageNamesToImagesMap Map, that maps image names (not paths!) to image objects
  * @return boolean value indicating whether the info file could be written or not
  */
bool VTTInfoFile::WriteInitialFile( const string &directoryPath, const map<string, VTTImage*> *imageNamesToImagesMap )
{
	ofstream info_file;
	info_file.open( directoryPath + VTT_FILE_SEPARATOR + "textureAtlasInfo.txt" );

	info_file << "# Virtual Texture Atlas Info File, generated by VTTools by Michael Birsak\n#\n";
	info_file << "# <imageName>/<occurrence>/(<areaMinX>/<areaMinY>/<areaExclMaxX>/<areaExclMaxY>/){<occurrence>}<chTime_hour>/<chTime_isdst>/<chTime_mday>/<chTime_min>/<chTime_mon>/<chTime_sec>/<chTime_wday>/<chTime_yday>/<chTime_year>\n";

	const VTTImage *image;
	const UINT32 *area;
	const struct tm *time;

	for (map<string, VTTImage*>::const_iterator it = imageNamesToImagesMap->begin();
		it != imageNamesToImagesMap->end(); it++)
	{
		image = it->second;

		info_file << it->first << "/" << image->GetPackNodeVector().size() << "/";

		for (vector<VTTPackNode*>::const_iterator it = image->GetPackNodeVector().begin();
			it != image->GetPackNodeVector().end(); it++)
		{
			area = (*it)->GetArea();

			info_file << area[0] << "/" << area[1] << "/" << area[2] << "/"
				<< area[3] << "/";
		}

		time = image->GetLastChangingDate();
		info_file << time->tm_hour << "/"
			<< time->tm_isdst << "/" << time->tm_mday << "/"
			<< time->tm_min << "/" << time->tm_mon << "/"
			<< time->tm_sec << "/" << time->tm_wday << "/"
			<< time->tm_yday << "/" << time->tm_year << "\n";
	}

	info_file.close();
	return true;
}